//*****************************************************************************
//
// Author		: Konstantin Chizhov
// Date			: 2013
// All rights reserved.

// Redistribution and use in source and binary forms, with or without modification, 
// are permitted provided that the following conditions are met:
// Redistributions of source code must retain the above copyright notice, 
// this list of conditions and the following disclaimer.

// Redistributions in binary form must reproduce the above copyright notice, 
// this list of conditions and the following disclaimer in the documentation and/or 
// other materials provided with the distribution.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//*****************************************************************************

#pragma once

#ifndef _MSP43_TIMER_H
#define _MSP43_TIMER_H

namespace Mcucpp
{
	namespace Timers
	{
		class Timer0
		{
			public:
			typedef unsigned short DataT;
			static const unsigned short MaxValue = 0xffff;
					
			enum ClockDivider
			{
				DivStop = 0, 
				Div1 = ID_0, 
				Div2 = ID_1, 
				Div4 = ID_2, 
				Div8 = ID_3 
			};

			enum {ClockDividerMask = ~Div8};
					
			enum ClockSrc
			{ 
			  ExtRising = TASSEL_0,
			  AuxClock = TASSEL_1,
			  MainClock = TASSEL_2,          
			  ExtFailing = TASSEL_3
			};

			enum {ClockSrcMask = ~ExtFailing};
					
			enum TimerMode
			{
				Normal = MC_2,
				ClearOnMatch0 = MC_1,
				UpDown = MC_3
			};
					
			enum {TimerModeMask = ~UpDown};

			template<unsigned Number> struct Divider;

			static void Set(DataT val)
			{
				TAR = val;
			}

			static DataT Get()
			{
				return TAR;
			}

			static void Stop()
			{
				TACTL = 0;
			}

			static void Clear()
			{
				TACTL |= TACLR;
			}

			static void Start(ClockDivider divider, ClockSrc clockSrc = MainClock, TimerMode mode = Normal)
			{
				TACTL = (TACTL & (ClockDividerMask | ClockSrcMask | TimerModeMask)) 
							  | divider | clockSrc | mode;
			}

			static void EnableInterrupt()
			{
				TACTL |= TAIE;
			}

			static bool IsInterrupt()
			{
				return TACTL & TAIFG;
			}

			static void ClearInterruptFlag()
			{
				TACTL |= TAIFG;
			}

			static void SetMode(TimerMode mode)
			{
				TACTL = (TACTL & TimerModeMask) | mode;
			}

			template<int number> class OutputCompare;
			template<int number> class InputCapture;
		};
		
		template<> struct Timer0::Divider <0> { static const ClockDivider value = Div1;	enum {Div = 1}; };
		template<> struct Timer0::Divider <1> { static const ClockDivider value = Div2;	enum {Div = 2}; };
		template<> struct Timer0::Divider <2> { static const ClockDivider value = Div4;	enum {Div = 4}; };
		template<> struct Timer0::Divider <3> { static const ClockDivider value = Div8;	enum {Div = 8}; };
		
		template<> class Timer0::OutputCompare<0>
		{
		public:
			enum OutputMode
			{
				OutNone = OUTMOD_0,
				OutSet = OUTMOD_1,
				OutTogleReset = OUTMOD_2,
				OutSetReset = OUTMOD_3,
				OutTogle = OUTMOD_4,
				OutReset = OUTMOD_5,
				OutTogleSet = OUTMOD_6,
				OutResetSet = OUTMOD_7
			};
			enum {OutputModeMask = ~OutResetSet};
			
			static void Set(DataT val)
			{
				TACCR0 = val;
			}

			static DataT Get()
			{
				return TACCR0;
			}

			static void EnableInterrupt()
			{
				TACCTL0 |= CCIE;
			}

			static bool IsInterrupt()
			{
				return TACCTL0 & CCIFG;
			}
		
			static void ClearInterruptFlag()
			{
				TACCTL0 |= CCIFG;
			}
			
			static void SetMode(OutputMode mode)
			{
				TACCTL0 = (TACCTL0 & OutputModeMask) | mode;
			}
		};
		
		template<> class Timer0::OutputCompare<1>
		{
		public:
			enum OutputMode
			{
				OutNone = OUTMOD_0,
				OutSet = OUTMOD_1,
				OutTogleReset = OUTMOD_2,
				OutSetReset = OUTMOD_3,
				OutTogle = OUTMOD_4,
				OutReset = OUTMOD_5,
				OutTogleSet = OUTMOD_6,
				OutResetSet = OUTMOD_7
			};
			enum {OutputModeMask = ~OutResetSet};
			
			static void Set(DataT val)
			{
				TACCR1 = val;
			}

			static DataT Get()
			{
				return TACCR1;
			}

			static void EnableInterrupt()
			{
				TACCTL1 |= CCIE;
			}

			static bool IsInterrupt()
			{
				return TACCTL1 & CCIFG;
			}
		
			static void ClearInterruptFlag()
			{
				TACCTL1 |= CCIFG;
			}
			
			static void SetMode(OutputMode mode)
			{
				TACCTL1 = (TACCTL1 & OutputModeMask) | mode;
			}
		};
		
	}
}
#endif